package ThreadingSafety;

/**
 *  wait 和 notify : 操作必须是要针对 同一个 对象 !!!!!
 *       wait操作,内部实际上执行了三件事 :
 *          1. 释放当前锁. (要释放锁的前提是要有锁,即写在synchronized里面, 否则就会抛异常)
 *          2. 进行等待通知
 *          3. 满足条件被唤醒 (等待有其他线程调用 notify 来唤醒它) ,然后尝试重新获取锁.
 *
 *       notify : 随机唤醒 一个 wait ; notifyAll : 唤醒全部的 wait.
 *       notify 也要写在 synchronized 里边~~~
 *
 *       wait 和 notify 是Object类的方法,而Object类是所有类的父类,因此
 *       任意一个类都可以调用这两个方法~~
 *
 *       线程1 先调用了wait,线程2再调用notify, 此时wait会被唤醒, 这没问题~~
 *       但如果是线程2先调用了notify,线程1后调用wait是不会 有异常,也不会有副作用的(即便没有线程调用wait也是如此),
 *       只不过如果后续没有线程调用notify的话,线程1的wait就不会被唤醒,将一直处于阻塞状态,只是写代码值得注意的~~~
 *
 */
public class TestDemo17 {

    public static void main(String[] args) throws InterruptedException {
        Object object = new Object(); // 为了保证以下的操作都是 同一个对象, 在此借用 Object 类.

        // 线程1 调用 wait
        Thread thread1 = new Thread(() -> {
            while(true) {
                synchronized (object) {  // 调用 wait 前要 先 加锁
                    System.out.println("线程1的wait 之前");
                    try {   // 需要保证加锁的对象和调用wait的对象是同一个对象. 也要保证调用wait的对象 和 调用 notify的对象是同一个对象!!

                        object.wait();  // 代码将阻塞在 wait 直到另一个线程调用notify将wait唤醒!
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    // 此处写的代码一定是在 notify 之后执行的
                    System.out.println("线程1的wait 之后");
                }
                //
            }
        });
        thread1.start();

        // 线程3 调用 wait
        Thread thread3 = new Thread(() -> {
            while(true) {
                synchronized (object) {  // 调用 wait 前要 先 加锁
                    System.out.println("线程3的wait 之前");
                    try {   // 需要保证加锁的对象和调用wait的对象是同一个对象. 也要保证调用wait的对象 和 调用 notify的对象是同一个对象!!

                        object.wait();  // 代码将阻塞在 wait 知道另一个线程 调用
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    // 此处写的代码一定是在 notify 之后执行的

                    System.out.println("线程3的wait 之后");
                }

            }
        });
        thread3.start();


        Thread.sleep(1000); // 为了防止 抢占式执行, 保证先调用wait , 再调用notify.

        // 线程2 调用 notify,
        Thread thread2 = new Thread(()-> {
            while(true) {
                synchronized (object) {
                    System.out.println("线程2的 notify之前");

                    // 此处写代码一定是在 wait 被唤醒之前执行的

                    object.notify();  // 随机唤醒线程1 和 线程3 中的一个
                    System.out.println("线程2的 notify之后");
                }

                try {
                    Thread.sleep(10_000);  // 为了看的更直观, 在此等待 10s
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        thread2.start();
    }
}
//
